package com.jvm;

import com.jvm.classfile.model.Class;
import com.jvm.classfile.model.CpInfo;
import com.jvm.classfile.model.Method;
import com.jvm.interpreter.*;
import com.sun.org.apache.bcel.internal.Const;

/**
 * @program: diy_simple_jvm
 * @description:
 * @author: CaoYong
 * @create: 2021-07-08 16:05
 **/
public class Utils {

    public static String getUtf8(CpInfo[] cp, int utf8Idx) {
        final CpInfo utf8 = cp[utf8Idx];
        if (utf8.tag.val != Const.CONSTANT_Utf8) {
            throw new IllegalStateException("unexpect tag");
        }

        return new String(utf8.info);
    }

    public static String getClassName(CpInfo[] cp, int classIdx) {
        final CpInfo classInfo = cp[classIdx];
        if (classInfo.tag.val != Const.CONSTANT_Class) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = classInfo.info;
        final int nameIndex = (info[0] << 8) + (info[1] & 0xff);
        return getUtf8(cp, nameIndex);
    }

    /**
     * 从常量池 NameAndType 项中获取 Name.
     */
    public static String getNameFromNameAndType(CpInfo[] cp, int idx) {
        final CpInfo item = cp[idx];
        if (item.tag.val != Const.CONSTANT_NameAndType) {
            throw new IllegalStateException("un expect tag");
        }
        final byte[] info = item.info;
        final int uidx = (info[0] << 8) + (info[1] & 0xff);
        return getUtf8(cp, uidx);
    }

    /**
     * 从常量池 NameAndType 项中获取 type.
     */
    public static String getDescriptorFromNameAndType(CpInfo[] cp, int idx) {
        final CpInfo item = cp[idx];
        if (item.tag.val != Const.CONSTANT_NameAndType) {
            throw new IllegalStateException("un expect tag");
        }
        final byte[] info = item.info;
        final int uidx = (info[2] << 8) | (info[3] & 0xff);
        return getUtf8(cp, uidx);
    }

    /**
     * 从常量池 FiledRef 中获取 类名
     */
    public static String getClassNameFromFieldRef(CpInfo[] cp, int fieldRefIdx) {
        final CpInfo item = cp[fieldRefIdx];
        if (item.tag.val != Const.CONSTANT_Fieldref) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = item.info;
        final int clzIdx = (info[0] << 8) + (info[1] & 0xff);
        return getClassName(cp, clzIdx);
    }

    /**
     * 从常量池 FiledRef 中获取 字段名
     */
    public static String getNameFromFieldRef(CpInfo[] cp, int fieldRefIdx) {
        final CpInfo item = cp[fieldRefIdx];
        if (item.tag.val != Const.CONSTANT_Fieldref) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = item.info;
        final int nameIndex = (info[2] << 8) | (info[3] & 0xff);
        return getNameFromNameAndType(cp, nameIndex);
    }

    /**
     * 从常量池 FiledRef 中获取 字段描述符
     */
    public static String getDescriptorFromFieldRef(CpInfo[] cp, int fieldRefIdx) {
        final CpInfo item = cp[fieldRefIdx];
        if (item.tag.val != Const.CONSTANT_Fieldref) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = item.info;
        final int idx = (info[2] << 8) | (info[3] & 0xff);
        return getDescriptorFromNameAndType(cp, idx);
    }


    /**
     * 从常量池 MethodRef 中获取 类名
     */
    public static String getClassNameFromMethodRef(CpInfo[] cp, int refIdx) {
        final CpInfo item = cp[refIdx];
        if (item.tag.val != Const.CONSTANT_Methodref) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = item.info;
        final int clzIdx = (info[0] << 8) + (info[1] & 0xff);
        return getClassName(cp, clzIdx);
    }

    /**
     * 从常量池 MethodRef 中获取 方法名
     */
    public static String getNameFromMethodRef(CpInfo[] cp, int refIdx) {
        final CpInfo item = cp[refIdx];
        if (item.tag.val != Const.CONSTANT_Methodref) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = item.info;
        final int nameIndex = (info[2] << 8) | (info[3] & 0xff);
        return getNameFromNameAndType(cp, nameIndex);
    }

    /**
     * 从常量池 MethodRef 中获取 方法描述符
     */
    public static String getDescriptorFromMethodRef(CpInfo[] cp, int refIdx) {
        final CpInfo item = cp[refIdx];
        if (item.tag.val != Const.CONSTANT_Methodref) {
            throw new IllegalStateException("un expect tag");
        }

        final byte[] info = item.info;
        final int idx = (info[2] << 8) | (info[3] & 0xff);
        return getDescriptorFromNameAndType(cp, idx);
    }

    /**
     * 判定访问标志是否是 static
     */
    public static boolean isStatic(int accessFlags) {
        return (accessFlags & Const.ACC_STATIC) != 0;
    }

    /**
     * 根据方法描述符解析得到方法参数占用的 slot 大小
     * @param descriptor 方法描述符
     * @return size of method descriptor
     * ()V => 0
     * (JDI)V => 5
     * ([I[[I[Ljava/lang/String;)V => 3
     */
    public static int getArgSlotSize(String descriptor) {
        int slots = 0;
        final int len = descriptor.length();
        for (int i = 1; i < len; i++) { // 从 1 开始，跳过起始的 (
            final char ch = descriptor.charAt(i);
            if (ch == ')') { // 以 ）结束。
                break;
            }
            if (ch == 'J' || ch == 'D') { // long , double 占两个 slot
                slots += 2;
            } else {
                if (ch == '[') { // 数组
                    while (descriptor.charAt(i + 1) == '[') { // 多维数组
                        i++;
                    }
                    i++;
                }
                if (descriptor.charAt(i) == 'L') { // 类
                    while (descriptor.charAt(i + 1) != ';') { // 类结束为 ；
                        i++;
                    }
                    i++;
                }
                slots += 1; // 除了 long , double 都占用 1 个 slot
            }
        }
        return slots;
    }

    /**
     * 类的初始化
     * @param clazz
     */
    public static void clinit(Class clazz) {
        if(clazz.stat >= com.jvm.classfile.Const.CLASS_INITING) {
            return;
        }

        if (clazz.getSuperClass() != null) {
            clinit(clazz.getSuperClass());
        }

        final Method clientMethod = clazz.getSpecialStaticMethod("<client>", "()V");
        if (clientMethod == null) {
            clazz.stat = com.jvm.classfile.Const.CLASS_LINKED;
            return;
        }

        clazz.stat = com.jvm.classfile.Const.CLASS_INITING;

        Frame newFrame = new Frame(clientMethod);
        Interpreter.execute(newFrame);
        clazz.stat = com.jvm.classfile.Const.CLASS_INITED;
    }

    /**
     * 返回操作
     * @param slotSize 返回值占用 slot 大小
     */
    public static void doReturn(int slotSize) {
        final ExecEnv env = MetaSpace.getMainEnv();
        final Frame old = env.popFrame();

        // 解释器同步执行方法的结束条件
        if (old.stat == com.jvm.classfile.Const.FAKE_FRAME) {
            old.stat = com.jvm.classfile.Const.FAKE_FRAME_END;
        }

        if (slotSize == 0) {
            return;
        }

        final Frame now = env.topFrame();
        if (slotSize == 1) {
            now.push(old.pop());
            return;
        }

        if (slotSize == 2) {
            final Slot v2 = old.pop();
            final Slot v1 = old.pop();
            now.push(v1);
            now.push(v2);
            return;
        }

        throw new IllegalStateException();
    }

    public static void doReturn0() {
        doReturn(0);
    }

    public static void doReturn1() {
        doReturn(1);
    }

    public static void doReturn2() {
        doReturn(2);
    }

    public static void invokeMethod(Method method) {


        Frame newFrame = new Frame(method);
        final ExecEnv env = MetaSpace.getMainEnv();
        final Frame old = env.topFrame();

        // 传参
        final int slots = method.getArgSlotSize();
        for (int i = slots - 1; i >= 0; i--) {
            newFrame.set(i, old.pop());
        }

        env.pushFrame(newFrame);

//        if (Utils.isNative(method.accessFlags)) {
//            final String key = method.getKey();
//            NativeMethod nm = MetaSpace.findNativeMethod(key);
//            if (nm == null) {
//                throw new IllegalStateException("not found native method: " + key);
//            }
//            nm.eval(MetaSpace.getMainEnv().topFrame());
//        } else {
//            Frame newFrame = new Frame(method);
//            final ExecEnv env = MetaSpace.getMainEnv();
//            final Frame old = env.topFrame();
//
//            // 传参
//            final int slots = method.getArgSlotSize();
//            for (int i = slots - 1; i >= 0; i--) {
//                newFrame.set(i, old.pop());
//            }
//
//            env.pushFrame(newFrame);
//        }
    }
}
